﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
using Microsoft.AspNetCore.Razor.Language.Intermediate;

namespace Microsoft.AspNetCore.Razor.Language;

/// <summary>
/// Extension methods to <see cref="IRazorEngineBuilder" />.
/// </summary>
[Obsolete("This class is obsolete and will be removed in a future version. The recommended alternative is " + nameof(RazorProjectEngineBuilderExtensions) + ".")]
public static class RazorEngineBuilderExtensions
{
    /// <summary>
    /// Adds the specified <see cref="DirectiveDescriptor"/>.
    /// </summary>
    /// <param name="builder">The <see cref="IRazorEngineBuilder"/>.</param>
    /// <param name="directive">The <see cref="DirectiveDescriptor"/> to add.</param>
    /// <returns>The <see cref="IRazorEngineBuilder"/>.</returns>
    public static IRazorEngineBuilder AddDirective(this IRazorEngineBuilder builder, DirectiveDescriptor directive)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }

        if (directive == null)
        {
            throw new ArgumentNullException(nameof(directive));
        }

        var directiveFeature = GetDirectiveFeature(builder);
        directiveFeature.Directives.Add(directive);

        return builder;
    }

    /// <summary>
    /// Adds the specified <see cref="ICodeTargetExtension"/>.
    /// </summary>
    /// <param name="builder">The <see cref="IRazorEngineBuilder"/>.</param>
    /// <param name="extension">The <see cref="ICodeTargetExtension"/> to add.</param>
    /// <returns>The <see cref="IRazorEngineBuilder"/>.</returns>
    public static IRazorEngineBuilder AddTargetExtension(this IRazorEngineBuilder builder, ICodeTargetExtension extension)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }

        if (extension == null)
        {
            throw new ArgumentNullException(nameof(extension));
        }

        var targetExtensionFeature = GetTargetExtensionFeature(builder);
        targetExtensionFeature.TargetExtensions.Add(extension);

        return builder;
    }

    /// <summary>
    /// Sets the base type for generated types.
    /// </summary>
    /// <param name="builder">The <see cref="IRazorEngineBuilder"/>.</param>
    /// <param name="baseType">The name of the base type.</param>
    /// <returns>The <see cref="IRazorEngineBuilder"/>.</returns>
    public static IRazorEngineBuilder SetBaseType(this IRazorEngineBuilder builder, string baseType)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }

        var configurationFeature = GetDefaultDocumentClassifierPassFeature(builder);
        configurationFeature.ConfigureClass.Add((document, @class) => @class.BaseType = baseType);
        return builder;
    }

    /// <summary>
    /// Registers a class configuration delegate that gets invoked during code generation.
    /// </summary>
    /// <param name="builder">The <see cref="IRazorEngineBuilder"/>.</param>
    /// <param name="configureClass"><see cref="Action"/> invoked to configure
    /// <see cref="ClassDeclarationIntermediateNode"/> during code generation.</param>
    /// <returns>The <see cref="IRazorEngineBuilder"/>.</returns>
    public static IRazorEngineBuilder ConfigureClass(
        this IRazorEngineBuilder builder,
        Action<RazorCodeDocument, ClassDeclarationIntermediateNode> configureClass)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }

        if (configureClass == null)
        {
            throw new ArgumentNullException(nameof(configureClass));
        }

        var configurationFeature = GetDefaultDocumentClassifierPassFeature(builder);
        configurationFeature.ConfigureClass.Add(configureClass);
        return builder;
    }

    /// <summary>
    /// Sets the namespace for generated types.
    /// </summary>
    /// <param name="builder">The <see cref="IRazorEngineBuilder"/>.</param>
    /// <param name="namespaceName">The name of the namespace.</param>
    /// <returns>The <see cref="IRazorEngineBuilder"/>.</returns>
    public static IRazorEngineBuilder SetNamespace(this IRazorEngineBuilder builder, string namespaceName)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }

        var configurationFeature = GetDefaultDocumentClassifierPassFeature(builder);
        configurationFeature.ConfigureNamespace.Add((document, @namespace) => @namespace.Content = namespaceName);
        return builder;
    }

    private static IRazorDirectiveFeature GetDirectiveFeature(IRazorEngineBuilder builder)
    {
        var directiveFeature = builder.Features.OfType<IRazorDirectiveFeature>().FirstOrDefault();
        if (directiveFeature == null)
        {
            directiveFeature = new DefaultRazorDirectiveFeature();
            builder.Features.Add(directiveFeature);
        }

        return directiveFeature;
    }

    private static IRazorTargetExtensionFeature GetTargetExtensionFeature(IRazorEngineBuilder builder)
    {
        var targetExtensionFeature = builder.Features.OfType<IRazorTargetExtensionFeature>().FirstOrDefault();
        if (targetExtensionFeature == null)
        {
            targetExtensionFeature = new DefaultRazorTargetExtensionFeature();
            builder.Features.Add(targetExtensionFeature);
        }

        return targetExtensionFeature;
    }

    private static DefaultDocumentClassifierPassFeature GetDefaultDocumentClassifierPassFeature(IRazorEngineBuilder builder)
    {
        var configurationFeature = builder.Features.OfType<DefaultDocumentClassifierPassFeature>().FirstOrDefault();
        if (configurationFeature == null)
        {
            configurationFeature = new DefaultDocumentClassifierPassFeature();
            builder.Features.Add(configurationFeature);
        }

        return configurationFeature;
    }
}
